package lock;

/**
 * ReentrantLock(重入锁)
 * ReentrantLock 是一个可重入的互斥（/独占）锁，又称为“独占锁”。
 * ReentrantLock通过自定义队列同步器（AQS-AbstractQueuedSychronized，是实现锁的关键）来实现锁的获取与释放。
 * 其可以完全替代 synchronized 关键字。JDK 5.0 早期版本，其性能远好于 synchronized，但 JDK 6.0 开始，JDK 对 synchronized 做了大量的优化，使得两者差距并不大。
 * “独占”，就是在同一时刻只能有一个线程获取到锁，而其它获取锁的线程只能处于同步队列中等待，只有获取锁的线程释放了锁，后继的线程才能够获取锁。
 * “可重入”，就是支持重进入的锁，它表示该锁能够支持一个线程对资源的重复加锁。
 * 该锁还支持获取锁时的公平和非公平性选择。“公平”是指“不同的线程获取锁的机制是公平的”，而“不公平”是指“不同的线程获取锁的机制是非公平的”。
 */

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class ReentrantLockDemo implements Runnable {
    //声明一把锁，其中ReentrantLock（可重入的互斥锁）是Lock接口的一个实现
    public static Lock lock = new ReentrantLock();
    public static int i = 0;

    @Override
    public void run() {
        for (int j = 0; j < 10000; j++) {
            //然后在函数里面调用lock()方法声明同步代码块（临界区）
            lock.lock(); 
            //重入，连续两次获得同一把锁
            //lock.lock(); 
            try {
                i++;
            } finally {
                //最后在finally块中释放锁，重要！！！
                lock.unlock();
                //两次释放同一把锁
                //lock.unlock();
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        ReentrantLockDemo reenterLock = new ReentrantLockDemo();
        Thread t1 = new Thread(reenterLock);
        Thread t2 = new Thread(reenterLock);
        t1.start();
        t2.start();
        t1.join();//main线程放弃cpu控制权，并返回t1线程继续执行直到线程t1执行完毕
        t2.join();//main线程放弃cpu控制权，并返回t1线程继续执行直到线程t2执行完毕
        System.out.println(i);//t1、t2线程执行完后，才到主线程执行，相当于在main线程中同步t1、t2 线程，t1、t2执行完了，main线程才有执行的机会
    }

}

/*
与 synchronized 相比，重入锁有着显示的操作过程，何时加锁，何时释放，都在程序员的控制中。

为什么称作是“重入”？这是因为这种锁是可以反复进入的。将上面代码中注释部分去除注释，也就是连续两次获得同一把锁，两次释放同一把锁，这是允许的。

注意，获得锁次数与释放锁次数要相同，如果释放锁次数多了，会抛出 java.lang.IllegalMonitorStateException 异常；如果释放次数少了，相当于线程还持有这个锁，其他线程就无法进入临界区。

引出第一个问题：为什么 ReentrantLock 锁能够支持一个线程对资源的重复加锁？

除了简单的加锁、解锁操作，重入锁还提供了一些更高级的功能，下面结合实例进行简单介绍： 请看ReentrantLockDemo2
 */